{
	open Parser
	
	let prev_bras = ref []
	let add_open_bra bra = prev_bras := bra::(!prev_bras)
	let remove_open_bra bra =
		match (!prev_bras) with
			| [] -> false
			| prev_bra::tail -> if bra == prev_bra then begin prev_bras := tail; true end else false
	let get_last_bra () = 
		match (!prev_bras) with
			| [] -> ' '
			| prev_bra::tail -> prev_bra
	let has_more_open_bras () =
		match (!prev_bras) with
			| [] -> false
			| _::_ -> true
	let rec list_to_string l =
		match l with 
			| [] -> ";;\n"
			| t::q -> (String.make 1 t)^", "^(list_to_string q)
	let print_bras () = print_string (list_to_string (!prev_bras))
	
	let print_token = function
		| EOF -> "eof"
	  | LPAR -> "("
		| RPAR -> ")"
		| LBRA -> "{"
		| RBRA -> "}"
		| PLUS -> "+"
		| MINUS -> "-"
		| TIMES -> "*"
		| DIV -> "/"
		| MOD -> "mod"
		| LT -> "<"
		| LE -> "<="
		| GT -> ">"
		| GE -> ">="
		| EQUALS -> "=="
		| NOTEQUALS -> "!="
		| NOT -> "!"
		| AND -> "and"
		| OR -> "or"
		| CLASS -> "class"
		| EXTENDS -> "extends"
		| TRUE -> "true"
		| FALSE -> "false"
		| IF -> "if"
		| ELSE -> "else"
		| IN -> "in"
		| INSTANCEOF -> "instanceof"
		| NEW -> "new"
		| NULL -> "null"
		| STATIC -> "static"
		| THIS -> "this"
		| ASSIGN -> "="
		| COMMA -> ","
		| SEMICOLON -> ";"
		| POINT -> "."
		| INT i -> string_of_int i
		| STRING s -> s
		| VARIDENT s -> s
		| CLASSIDENT s -> s

	let lex f = let a g lexbuf = let tmp = (g lexbuf) in print_endline (print_token tmp); tmp in (a f)
}


let letter = ['a'-'z' 'A'-'Z']
let lowerletter = ['a'-'z']
let upperletter = ['A'-'Z']
let digit = ['0'-'9']
let ident = lowerletter (letter | digit | '_')*
let classident = upperletter (letter | digit | '_')*
let space = [' ' '\t']
let newline = '\n' | '\r' | "\r\n" | "\n\r"
let stringcontent = ([^'"'] | ("\\\""))*

rule nexttoken = parse
	| newline { Location.incr_line lexbuf; nexttoken lexbuf }
	| space+ { nexttoken lexbuf }
	| eof {if has_more_open_bras () then Error.expected_right_bracket (get_last_bra ()) (Location.curr lexbuf); EOF }
	| '{' { add_open_bra ('}'); LBRA }
	| '}' { if remove_open_bra('}') != true then Error.expected_right_bracket (get_last_bra ()) (Location.curr lexbuf); RBRA }
	| '(' { add_open_bra (')'); LPAR }
	| ')' { if remove_open_bra(')') != true then Error.expected_right_bracket (get_last_bra ()) (Location.curr lexbuf); RPAR }
	| ',' { COMMA }
  | ';' { SEMICOLON }
  | '.' { POINT }
  | '!' { NOT }
	| '+' { PLUS }
  | '-' { MINUS }
  | '/' { DIV }
  | '*' { TIMES }
  | '%' { MOD }
  | '=' { ASSIGN }
  | '<' { LT }
  | '>' { GT }
  | "<=" { LE }
  | ">=" { GE }
  | "==" { EQUALS }
  | "!=" { NOTEQUALS }
  | "&&" { AND }
  | "||" { OR }
	| "class" { CLASS }
	| "extends" { EXTENDS }
	| "true" { TRUE }
	| "false" { FALSE }
	| "if" { IF }
	| "else" { ELSE }
	| "in" { IN }
	| "instanceof" { INSTANCEOF }
	| "new" { NEW }
	| "null" { NULL }
	| "static" { STATIC }
	| "this" { THIS }
	| digit+ as nb { INT (int_of_string nb) }
	| '"' { STRING (nextcharinstring (Location.curr lexbuf) "" lexbuf) }
	| "//" { nextcharinonelinecomment lexbuf; nexttoken lexbuf }
	| "/*" { nextcharincomment (Location.curr lexbuf) lexbuf; nexttoken lexbuf }
	| ident { VARIDENT (Lexing.lexeme lexbuf) }
	| classident { CLASSIDENT (Lexing.lexeme lexbuf) }
	| _ as char { Error.illegal_char char (Location.curr lexbuf) } 
and nextcharinstring beginning currentstring = parse
	| eof | '\n' | '\r' { Error.unterminated_string beginning }
	| "\"" { currentstring }
	| "\\n" { nextcharinstring beginning (currentstring^"\n") lexbuf }
	| "\\r" { nextcharinstring beginning (currentstring^"\r") lexbuf }
	| "\\t" { nextcharinstring beginning (currentstring^"\t") lexbuf }
	| "\\\"" { nextcharinstring beginning (currentstring^"\"") lexbuf }
	| "\\\\" { nextcharinstring beginning (currentstring^"\\") lexbuf }
	| '\\' _ { Error.illegal_escape_char (Location.curr lexbuf) }
	| _ as char { nextcharinstring beginning (currentstring^(String.make 1 char)) lexbuf }
and nextcharinonelinecomment = parse
	| newline { Location.incr_line lexbuf; () }
	| _ { nextcharinonelinecomment lexbuf }
and nextcharincomment beginning = parse
	| eof { Error.unterminated_comment beginning }
	| "*/" { () }
	| newline { Location.incr_line lexbuf; nextcharincomment beginning lexbuf }
	| _ { nextcharincomment beginning lexbuf }
